#include <system.h>
#include "usart.h"
#include "globalopts.h"

#include "gc_menu.h"
#include "eeprom.h"
#include "lcd.h"
#include "menu.h"

#define PASSWORD 12345
#define TIMEOUT_MINS 5
//internal vars
unsigned char attribute;
unsigned char keg_select;
unsigned short orig_val;
unsigned char timeout_mins = 0;
unsigned short current_pw = 0;
//external vars
union keginfo_u keginfo[NUM_KEGS];


//EEPROM Initial Values
#if 1
//#pragma doesn't support #defined addresses, so we can't use the above defines
#pragma DATA _EEPROM,	0xC0, 0x7F, /*TANK1_ZERO_WEIGHT  = 130 << 6*/ \
			0xBB, 0x0B, /*TANK1_EMPTY_WEIGHT =  40 << 6*/ \
			0xFC, 0x2A, /*TANK1_FULL_WEIGHT  = 400 << 6*/ \
			40,         /*TANK1_MAX_PINTS    = 40      */ \
\
			0x80, 0x20, /*TANK2_ZERO_WEIGHT  = 130 << 6*/ \
			0x00, 0x0A, /*TANK2_EMPTY_WEIGHT =  40 << 6*/ \
			0x00, 0x64, /*TANK2_FULL_WEIGHT  = 400 << 6*/ \
			40,         /*TANK2_MAX_PINTS    = 40      */ \
\
			0x80, 0x20, /*TANK3_ZERO_WEIGHT  = 130 << 6*/ \
			0x00, 0x0A, /*TANK3_EMPTY_WEIGHT =  40 << 6*/ \
			0x00, 0x64, /*TANK3_FULL_WEIGHT  = 400 << 6*/ \
			28          /*TANK3_MAX_PINTS    = 28      */
#endif

rom	char *menu_str =
	"Go to Prev Menu "
        "Zero            "
	"Empty Weight    "
	"Full Weight     "
	"Max Pints       "
//////////////////////////
        "Keg1  Keg2  Keg3"
        "Debug           "
	"Password        ";


void sendFromRom(char val, unsigned char len)
{
	unsigned short addr;
	char pos, end;
	pos = val << 4;						//val * 16 = ptr into 16char strings
	end = pos + len;
	while(pos < end) {
		sendChar(menu_str[pos++]);
	}
}

void compute_units_per_pint(char keg)
{
	unsigned char i;
	unsigned short pints;
	unsigned char eeprom_loc = keg * EEPROM_BYTES_PER_KEG;
	unsigned char *ptr = keginfo[keg].bytes;
	struct keginfo_s *info = (struct keginfo_s *)ptr;

        for (i = 0; i < EEPROM_BYTES_PER_KEG; i++) {
		*ptr = read_eeprom(eeprom_loc++);
		ptr++;
	}
	pints = info->full_weight - info->empty_weight;
	pints /= info->max_pints;
	//count_per_pint[keg] = pints;
	info->count_per_pint = pints;
}

void compute_current_pints(unsigned char keg, char first_meas)
{
	struct keginfo_s *info = &keginfo[keg].d;
	unsigned short this_weight;
	unsigned short min_weight = info->zero_weight + info->empty_weight;
	unsigned short adval = adresh << 8;
	adval |= adresl;
	if(first_meas) {
		info->half_weight = adval;
		return;
	}
	this_weight = info->half_weight - adval;

	//tank_weight is a modified moving average w/ n = 64
	//This allows us to do the entire caluclation in 16bits losing no precision
	//Because the A/D is only 10bits
	//Note that the A/D is right aligned, but tank_weight is left-aligned
	//Avg = (OldAvg * 63 + NewValue) / 64
	//Avg = tw - (tw >> 6) + (new_val)
	adval = info->tank_weight - (info->tank_weight >> 6);
	adval += this_weight;
	info->tank_weight = adval;
	
	if(adval < min_weight) {
		info->pints = 0;
		return;
	}
	adval -= min_weight;
	//Note this is a 16bit / 16bit division, we may want to make it 16bits / 8 bits
	info->pints = adval / info->count_per_pint;

	//Note that theoretically this could overflow, but it is cheaper to check
	//pints after computation than to compare adval to zero_weight + full_weight
	if (info->pints > info->max_pints)
		info->pints = info->max_pints;
}

void show_id()
{
	unsigned char i;
	tblptru = _DEVID1 >> 16;
	tblptrh = _DEVID1 >> 8;
	tblptrl = _DEVID1;
	asm tblrd*
	sendDecimalBytePad((0x1f & tablat), 1);
	sendChar(' ');
	for(i = 0; i < 7; i++) {
		char c = version_str[i];
		if(! c)
			return;
		sendChar(c);
	}
}

void menu_reset(void)
{
	timeout_mins = 0;
}
void menu_1min(void)
{
	if(TIMEOUT_MINS < ++timeout_mins) {
		unsigned char i;
		clear_bit(debug_flags, DEBUG_AD);
		current_pw = 0;
		for(i = 0; i < NUM_KEGS; i++)
			compute_units_per_pint(i);
		timeout_mins = 0;
	}

}

void menu_1sec(void)
{
	if(current_pw != PASSWORD) {
		clear_bit(debug_flags, DEBUG_AD);
	}
}

void show_menu(void)
{
	if(! gm_menu_lvl)
		return;
	ClearScreen();
	if(gm_menu_lvl < 3 && ! (unsigned char)gm_menu_sel) {
		sendFromRom( 0, 16);
		return;
	}
	if(gm_menu_lvl == 1) {
		if((unsigned char)gm_menu_sel == NUM_KEGS + 1) {
			sendFromRom(6, 6);
			show_id();
			return;
		}
		sendFromRom(5, 3);
		sendChar(' ');
		sendChar('0' + (unsigned char)gm_menu_sel);
		return;
	}
	if(gm_menu_lvl == 4) {
		sendFromRom( 7, 16);
		SetPosLine2();
		if(attribute != 5)
			sendDecimalShort(current_pw);
		sendChar('0' + (unsigned char)gm_menu_sel);
		return;
	}
	sendChar('#');
	sendChar('1' + keg_select);
	sendChar(' ');
	if(gm_menu_lvl == 2) 
		attribute=(unsigned char)gm_menu_sel - 1;
	sendFromRom(attribute + 1, 13);
	if(gm_menu_lvl == 3) {
		SetPosLine2();
		sendDecimalShort(orig_val);
		sendChar(' ');
		sendDecimalShort(gm_menu_sel);
	}
}


void set_menu_state(void)
{
	if(current_pw != PASSWORD && gm_menu_lvl != 4) {
		//Display password
		current_pw = 0;
		gm_menu_lvl = 4;
		gm_menu_sel = 0;
		gm_max_count = 9;
		attribute = 5; //attribute is current location
		gm_step = 1;
		return;
	}
	if(gm_menu_lvl == 0) {
		//Display menu
		gm_menu_lvl = 1;
		gm_menu_sel = 1;
		gm_max_count=NUM_KEGS + 1;
		gm_step = 1;
		return;
	}
	if(gm_menu_lvl == 1) {
		//Keg select
                if(! (unsigned char)gm_menu_sel) {
			gm_menu_lvl = 0;
			return;
		}
		if(gm_menu_sel == NUM_KEGS + 1) {
			gm_menu_lvl = 0;
			toggle_bit(debug_flags, DEBUG_AD);
			return;
		}
                keg_select=(unsigned char)gm_menu_sel - 1;
		gm_menu_sel = 1;
                gm_max_count=KEG_ATTRIBUTES;
                gm_step = 1;
		gm_menu_lvl++;
          	return;
	} 
	if(gm_menu_lvl == 2) {
		//Attribute select
		struct keginfo_s *info;
                if(! (unsigned char)gm_menu_sel) {
			gm_menu_lvl--;
			gm_menu_sel = keg_select + 1;
			gm_step = 1;
			gm_max_count=NUM_KEGS + 1;
			return;
		}
 		info = &keginfo[keg_select].d;
		attribute=(unsigned char)gm_menu_sel - 1;
		if(attribute < 3) {				//Set min/max weight
			gm_step      = 1 << 6;			// A/D Converter is 10bit left-aligned, so minimum movement is 2^6
			gm_max_count = 0xffff;
		} else {
			gm_step      = 1;
			gm_max_count = 0xff;
		}
		switch(attribute) {
			case 0:
				orig_val = info->zero_weight;
				gm_menu_sel = info->tank_weight;
				break;
			case 1:
				orig_val = info->empty_weight;
				gm_menu_sel = info->tank_weight- info->zero_weight;
				break;
			case 2:
				orig_val = info->full_weight;
				gm_menu_sel = info->tank_weight - info->zero_weight;
				break;
			case 3:
				orig_val     = info->max_pints;
				gm_menu_sel  = orig_val;
				break;
		}
		gm_menu_lvl++;
		return;
	}
	if(gm_menu_lvl == 3) {
		unsigned char eeprom_loc;
		eeprom_loc = (unsigned char)(keg_select * EEPROM_BYTES_PER_KEG) + (attribute << 1);
		write_eeprom(eeprom_loc, gm_menu_sel & 0xff);
		if(attribute < 3)
			write_eeprom(++eeprom_loc, gm_menu_sel >> 8);
		compute_units_per_pint(keg_select);
		gm_menu_sel=attribute + 1;
		gm_menu_lvl--;
		gm_step = 1;
		gm_max_count=KEG_ATTRIBUTES + 1;
		return;
	}
	if(gm_menu_lvl == 4) {
		current_pw = current_pw * 10;
		current_pw = current_pw + gm_menu_sel;
		gm_menu_sel = 0;
		attribute--;
		if(attribute == 0)
			gm_menu_lvl = 0;
		return;
	}
}
